﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace IceDog.SmallProject.EncryptionAndDecryption
{
    /// <summary>
    /// AES加密解密 .net Framework 版本
    /// </summary>
    public static class AESFramework
    {
        //盐字节数组,至少8个字节，否则报错
        private static byte[] saltBytes = new byte[9] { 14, 16, 27, 67, 189, 255, 104, 219, 122 };
        /// <summary>
        /// AES加密文本
        /// </summary>
        /// <param name="text">文本</param>
        /// <param name="key">自定义的秘钥key</param>
        /// <returns></returns>
        public static string AESEncryptText(string text, string key)
        {
            //待加密内容的字节数组
            byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(text);
            //密码字节数组
            byte[] passwordBytes = Encoding.UTF8.GetBytes(key);
            //key通过SHA256加密
            passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
            //加密后的字节数组
            byte[] bytesEncrypted = AESEncryptBytes(bytesToBeEncrypted, passwordBytes);
            //把字节数组转换为Base64编码
            string result = Convert.ToBase64String(bytesEncrypted);

            return result;
        }
        /// <summary>
        /// AES加密字节数组
        /// </summary>
        /// <param name="bytesToBeEncrypted">要被加密的字节数组</param>
        /// <param name="passwordBytes">密匙key字节数组</param>
        /// <returns></returns>
        private static byte[] AESEncryptBytes(byte[] bytesToBeEncrypted, byte[] passwordBytes)
        {
            //加密后的字节数组
            byte[] encryptedBytes = null;
            using var ms = new MemoryStream();
            using var AES = new RijndaelManaged();

            //密钥长度可以为128、196、256位
            AES.KeySize = 256;
            //区块大小只能是 128位
            AES.BlockSize = 128;
            //基于密码的密钥派生功能，PBKDF2
            var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10);
            //获取用于对称算法的密钥。
            AES.Key = key.GetBytes(32);
            //获取用于对称算法的初始化向量（IV）。
            AES.IV = key.GetBytes(16);
            //模式是密码块链
            AES.Mode = CipherMode.CBC;
            //加密流
            using var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
            cs.Close();
            encryptedBytes = ms.ToArray();
            return encryptedBytes;
        }

        /// <summary>
        /// AES解密文本
        /// </summary>
        /// <param name="text">文本</param>
        /// <param name="key">自定义的秘钥key</param>
        /// <returns></returns>
        public static string AESDecryptText(string text, string key)
        {
            //Base64编码转换为字节数组
            byte[] bytesToBeDecrypted = Convert.FromBase64String(text);
            //密码字节数组
            byte[] passwordBytes = Encoding.UTF8.GetBytes(key);
            //key通过SHA256加密
            passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
            //解密后的字节数组
            byte[] bytesDecrypted = AESDecryptBytes(bytesToBeDecrypted, passwordBytes);
            //从UTF8编码转换为字符串
            string result = Encoding.UTF8.GetString(bytesDecrypted);
            return result;
        }

        /// <summary>
        /// AES解密字节数组
        /// </summary>
        /// <param name="bytesToBeDecrypted">要被解密的文本</param>
        /// <param name="passwordBytes">密匙key字节数组</param>
        /// <returns></returns>
        private static byte[] AESDecryptBytes(byte[] bytesToBeDecrypted, byte[] passwordBytes)
        {
            byte[] decryptedBytes = null;
            using var ms = new MemoryStream();
            using var AES = new RijndaelManaged();
            AES.KeySize = 256;
            AES.BlockSize = 128;
            var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10);
            AES.Key = key.GetBytes(32);
            AES.IV = key.GetBytes(16);

            AES.Mode = CipherMode.CBC;
            using var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
            cs.Close();
            decryptedBytes = ms.ToArray();
            return decryptedBytes;
        }
    }
}
